/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package imp.clases;

import java.math.BigDecimal;

/**
 *
 * @author Angelical
 */
public class FuncionesFinancieras {

    public FuncionesFinancieras() {
    }

    /**
     * Calculate internal rate of return (IRR) using cash flows that occur at regular intervals, such as monthly or annually.
     * The internal rate of return is the interest rate received for an investment consisting of payments and receipts that occur at regular intervals.
     * <p>
     * Method: Newton-Raphson technique. Formula: sum(cashFlow(i) / (1 + IRR)^i)
     *
     * @param cashFlows Cash flow values. Must contain at least one negative value (cash paid) and one positive value (cash received).
     * @param estimatedResult Optional guess as start value (default (if value is Double.NaN): 0.1 = 10%; if value is negative: 0.5).
     * As the formula to calculate IRRs can have multiple solutions, an estimated result (guess) can help find the result we are looking for.
     * @return Internal rate of return (0.25 = 25%) or Double.NaN if IRR not computable.
     * @see <a href="http://forum.java.sun.com/thread.jspa?forumID=1&threadID=219441">IRR financial function in Java</a>
     * @see <a href="http://de.wikipedia.org/wiki/Interner_Zinsfu%C3%9F">Interner Zinsfu?</a>
     */
    static public double getIRR(final double[] cashFlows, final double estimatedResult) {
        double result = Double.NaN;

        if (cashFlows != null && cashFlows.length > 0) {
            //check if business startup costs is not zero:
            if (cashFlows[0] != 0.0) {
                final double noOfCashFlows = cashFlows.length;

                double sumCashFlows = 0.0;
                //check if at least 1 positive and 1 negative cash flow exists:
                int noOfNegativeCashFlows = 0;
                int noOfPositiveCashFlows = 0;
                for (int i = 0; i < noOfCashFlows; i++) {
                    sumCashFlows += cashFlows[i];
                    if (cashFlows[i] > 0) {
                        noOfPositiveCashFlows++;
                    } else if (cashFlows[i] < 0) {
                        noOfNegativeCashFlows++;
                    }
                }

                if (noOfNegativeCashFlows > 0 && noOfPositiveCashFlows > 0) { //at least 1 negative and 1 positive cash flow available?
                    //set estimated result:
                    double irrGuess = 0.1; //default: 10%
                    if (estimatedResult != Double.NaN) {
                        irrGuess = estimatedResult;
                        if (irrGuess <= 0.0) {
                            irrGuess = 0.5;
                        }
                    }

                    //initialize first IRR with estimated result:
                    double irr = 0.0;
                    if (sumCashFlows < 0) { //sum of cash flows negative?
                        irr = -irrGuess;
                    } else { //sum of cash flows not negative
                        irr = irrGuess;
                    }

                    //iteration:
                    final double minDistance = .0000001; //the smaller the distance, the smaller the interpolation error
                    final double cashFlowStart = cashFlows[0]; //business startup costs
                    final int maxIteration = 50;
                    boolean wasHi = false;
                    double cashValue = 0.0; //cash value (Kapitalwert)
                    for (int i = 0; i <= maxIteration; i++) { //for each iteration
                        //calculate cash value with current irr:
                        cashValue = cashFlowStart; //init with startup costs
                        for (int j = 1; j < noOfCashFlows; j++) { //for each cash floe
                            cashValue += cashFlows[j] / Math.pow(1.0 + irr, j);
                        }//next cash flow

                        if (Math.abs(cashValue) < 0.01) { //cash value is nearly zero
                            result = irr;
                            break;
                        }

                        //adjust irr for next iteration:
                        if (cashValue > 0.0) { //cash value > 0 => next irr > current irr
                            if (wasHi) {
                                irrGuess /= 2;
                            }

                            irr += irrGuess;

                            if (wasHi) {
                                irrGuess -= minDistance;
                                wasHi = false;
                            }
                        } else { //cash value < 0 => next irr < current irr
                            irrGuess /= 2;
                            irr -= irrGuess;
                            wasHi = true;
                        }

                        if (irrGuess <= minDistance) { //estimated result too small to continue => end calculation
                            result = irr;
                            break;
                        }
                    }//next iteration
                }//else: noOfNegativeCashFlows == 0 || noOfPositiveCashFlows == 0
            }//else: first cash flow is 0
        }//else: cashFlows unavailable

        return result;
    }//getIRR()

    /**
     * This class shows how to use the BigDecimal class for numeric
    calculation that require to be exact.
     * This particular class implements the formula of present value. The
    correct value was calculated using the
     * OpenOffice function :NPV(rate, range1, range2, ...). Only showing
    the first 20 decimals.
     * @author Jose Vicente Nunez Zuleta (josevnz@yahoo.com)
     * @version 0.1
     * @see http://www.investopedia.com/articles/03/101503.asp (check the
    example here. Also the calculator shows the wrong values: 209.21, not
    133.74)
     * @see http://www.developer.com/java/other/article.php/631281
     * @see http://www.developer.com/java/article.php/788311
     */
//public final class PV {
    private static BigDecimal power(BigDecimal number, int power) {
        BigDecimal powerc = number;
        for (int j = 1; j < power; j++) {
            powerc = powerc.multiply(number);
        }
        return powerc;
    }

    public static double regularNPV(double[] cashflows, double discountRate) {
        double pv = cashflows[0];
        for (int i = 1; i < cashflows.length; i++) {
            pv += cashflows[i] / Math.pow((1 + discountRate), i);
        }
        return pv;
    }

    public static BigDecimal correctNPV(BigDecimal[] cashflows,BigDecimal discountRate) {
        BigDecimal pv = cashflows[0];
        for (int i = 1; i < cashflows.length; i++) {
            pv = pv.add(cashflows[i].divide(power(discountRate.add(new BigDecimal("1.0")), i), BigDecimal.ROUND_HALF_EVEN));
        }
        return pv;
    }
//	public static void main(String [] args) {
//
//		// Calculation of the present value, "traditional" way
//		double [] cashflows     = { -1000.0f, 500.0f, 400.0f, 300.0f, 200.0f,
//100.0f };
//		double discountRate     = 10.0f / 100.0f;
//		double pv = regularNPV(cashflows, discountRate);
//
//		BigDecimal [] cashflows2 = new BigDecimal [6];
//		BigDecimal discountRate2 = new BigDecimal("0.1");
//		cashflows2[0] = new BigDecimal("-1000.0");
//		cashflows2[1] = new BigDecimal("500.0");
//		cashflows2[2] = new BigDecimal("400.0");
//		cashflows2[3] = new BigDecimal("300.0");
//		cashflows2[4] = new BigDecimal("200.0");
//		cashflows2[5] = new BigDecimal("100.0");
//		BigDecimal pv2 = correctNPV(cashflows2, discountRate2);
//
//		System.out.println("PV Theoric: $133.74645298694200000000");
//		System.out.println("PV Regular: $" + pv);
//		System.out.println("PV Proper:  $" + pv2.toString());
//	}
//}
}
